﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework;
using BlobTowerDefense.Pages;

using Matrix = Microsoft.Xna.Framework.Matrix;

namespace BlobTowerDefense.Tools
{
    public class Camera
    {
        private static Matrix view;
        private static Matrix projection;
        public static Vector3 camPos;
        private static Vector3 camLook;
        private static Vector3 camUp;
        private static Vector2 bottRight;
        private static Vector2 topLeft;
        private static Vector2 zoomMax;
        private static float orientation;
        private static float oldDistance; // pinch zoom
        private static float shakeMax, shakeActual, shakeReduction, shakeInc; // for shake gesture
        private static bool shakeMaximum;

        /// <summary>
        /// Constructor for creatinon default view and projection matrix.
        /// </summary>
        public Camera()
        {
            zoomMax = new Vector2(20f, 60f); //min and max zoom
            oldDistance = 0.0f;

            bottRight = new Vector2(30f, -50f);
            topLeft = new Vector2(-75f, 5f);

            camPos = new Vector3(-25f,-25f,35f);
            camLook = new Vector3(0.0f, 0.0f, 0.0f);
            camUp = new Vector3(-camPos.X, -camPos.Y, camPos.Z);
            camUp.Normalize();

            view = Matrix.CreateLookAt(camPos, camLook, camUp);
            UpdateProjection();

            shakeMax = 0.0f;
        }

        /// <summary>
        /// Get and set new view matrix
        /// </summary>
        public static Matrix ViewMatrix
        {
            get { return view; }
        }

        /// <summary>
        /// Get and set new projection matrix
        /// </summary>
        public static Matrix ProjectionMatrix
        {
            get { return projection; }
            set { projection = value; }
        }

        /// <summary>
        /// Update projection matrix when orientation change
        /// </summary>
        public static void UpdateProjection()
        {
            float fov;
            if (GamePage.Device.Viewport.AspectRatio > 1) //if landscape
                fov = MathHelper.ToRadians(35);
            else
                fov = MathHelper.ToRadians(45); //if portrait

            projection = Matrix.CreatePerspectiveFieldOfView(fov, GamePage.Device.Viewport.AspectRatio, 1, 1000);
            orientation = GamePage.Device.Viewport.AspectRatio;
        }

        /// <summary>
        /// Update view matrix for camera position.
        /// This will read gestures and move camera around the world.
        /// </summary>
        public void Update()
        {
            // look for orientation change
            if (orientation != GamePage.Device.Viewport.AspectRatio)
                UpdateProjection();

            if (shakeMax != 0.0f)
            {
                if(TouchPanel.IsGestureAvailable)
                    TouchPanel.ReadGesture(); // we need gestures out of seznam in shake we cant move
                Vector3 camPosBack, camLookBack;

                shakeActual += shakeInc;

                if (!shakeMaximum && Math.Abs(shakeActual) >= shakeMax)
                {
                    shakeMax += shakeReduction;
                    shakeInc *= -1.0f;
                    shakeMaximum = true;

                    if (shakeMax <= 0.0f)
                    {
                        shakeMax = 0.0f;
                    }
                }

                if (shakeMaximum && Math.Abs(shakeActual) <= shakeMax)
                {
                    shakeMaximum = false;
                }

                camPosBack = new Vector3(camPos.X, camPos.Y, camPos.Z);
                camLookBack = new Vector3(camLook.X, camLook.Y, camLook.Z);

                camPos.X += shakeActual;
                camLook.X += shakeActual;
                camPos.Y += shakeActual;
                camLook.Y += shakeActual;

                view = Matrix.CreateLookAt(camPos, camLook, camUp);

                camLook = camLookBack;
                camPos = camPosBack;
            }
            else
            {

                while (TouchPanel.IsGestureAvailable)
                {
                    Vector3 camPosBack, camLookBack;

                    GestureSample gs = TouchPanel.ReadGesture();

                    double sensitivity = Loader.Load_Sensitivity();
                
                    switch (gs.GestureType)
                    {
                        case GestureType.FreeDrag:
                            //backup old numbers
                            camPosBack = new Vector3(camPos.X, camPos.Y, camPos.Z);
                            camLookBack = new Vector3(camLook.X, camLook.Y, camLook.Z);
                            if (gs.Delta.X != 0) //move in X direction
                            {
                                camPos.X -= (gs.Delta.X * 0.5f * ((float)sensitivity / 50)) * camUp.X;
                                camLook.X -= (gs.Delta.X * 0.5f * ((float)sensitivity / 50)) * camUp.X;

                                camPos.Y += (gs.Delta.X * 0.5f * ((float)sensitivity / 50)) * camUp.Y;
                                camLook.Y += (gs.Delta.X * 0.5f * ((float)sensitivity / 50)) * camUp.Y;
                            }
                            if (gs.Delta.Y != 0) //move in Y direction
                            {
                                camPos.Y += (gs.Delta.Y * 0.5f * ((float)sensitivity /50)) * camUp.Y;
                                camLook.Y += (gs.Delta.Y * 0.5f * ((float)sensitivity / 50)) * camUp.Y;

                                camPos.X += (gs.Delta.Y * 0.5f * ((float)sensitivity / 50)) * camUp.X;
                                camLook.X += (gs.Delta.Y * 0.5f * ((float)sensitivity / 50)) * camUp.X;
                            }

                            //Camera boundary
                            if (camPos.X <= topLeft.X || camPos.X >= bottRight.X)
                            {
                                camPos.X = camPosBack.X;
                                camLook.X = camLookBack.X;
                            }
                            if (camPos.Y <= bottRight.Y || camPos.Y >= topLeft.Y)
                            {
                                camPos.Y = camPosBack.Y;
                                camLook.Y = camLookBack.Y;
                            }

                            break;

                        case GestureType.Pinch:
                            camPosBack = new Vector3(camPos.X, camPos.Y, camPos.Z);

                            Vector3 lookVec = new Vector3(camPos.X - camLook.X, camPos.Y - camLook.Y, camPos.Z - camLook.Z);
                            lookVec.Normalize();

                            float dist = (gs.Position - gs.Position2).Length();

                            if (oldDistance != 0.0f)
                            {
                                camPos.X += (oldDistance - dist) * lookVec.X;
                                camPos.Y += (oldDistance - dist) * lookVec.Y;
                                camPos.Z += (oldDistance - dist) * lookVec.Z;
                            }

                            oldDistance = dist;

                            //boundary for zoom
                            if (camPos.Z <= zoomMax.X || camPos.Z >= zoomMax.Y)
                            {
                                camPos = camPosBack;
                            }
                            break;
                    }
                    view = Matrix.CreateLookAt(camPos, camLook, camUp);
                }

            }
        }

        /// <summary>
        /// Animation for shake gesture
        /// </summary>
        /// <param name="max">Edge of animation</param>
        /// <param name="reduction">Reduction of max value when you reach edge</param>
        /// <param name="speed">Speed of animation</param>
        public static void ShakeGesture(float max = 3.5f, float reduction = 0.25f, float speed = 1.6f)
        {
            // Configurable variables
            shakeMax = max;
            shakeReduction = -reduction;
            shakeInc = speed;

            // default values
            shakeActual = 0.0f;
            shakeMaximum = false;
        }

    }   
}
